package com.zzl.bonds.ticket.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzl.bonds.ticket.constant.ApproveProcessEnum;
import com.zzl.bonds.ticket.constant.ProcessConstant;
import com.zzl.bonds.ticket.constant.type.ApproveTypeEnum;
import com.zzl.bonds.ticket.dao.TicketProcessDao;
import com.zzl.bonds.ticket.dto.ApUserShowInfo;
import com.zzl.bonds.ticket.dto.TicketProcessUpdateDto;
import com.zzl.bonds.ticket.dto.feign.ApprovalAddDto;
import com.zzl.bonds.ticket.dto.feign.ApprovalAuthPar;
import com.zzl.bonds.ticket.dto.feign.ApprovalSubmitDto;
import com.zzl.bonds.ticket.entity.TicketProcessEntity;
import com.zzl.bonds.ticket.exception.SysExceptionEnum;
import com.zzl.bonds.ticket.exception.SysExceptionFactory;
import com.zzl.bonds.ticket.feign.ProcessFeign;
import com.zzl.bonds.ticket.service.TicketProcessService;
import com.zzl.bonds.ticket.untils.StrUtils;
import com.zzl.bonds.ticket.untils.TicketUtils;
import com.zzl.general.common.exception.SysException;
import com.zzl.general.common.serurity.entity.User;
import com.zzl.general.common.utils.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.zzl.bonds.ticket.constant.FeigenStringConstants.APPROVAL_AUTH;
import static com.zzl.bonds.ticket.exception.SysExceptionEnum.*;

/**
 * 票据审批流程表
 *
 * @author wztsl
 * @date 2021-11-30 14:48:47
 */
@Service("ticketProcessService")
public class TicketProcessServiceImpl extends ServiceImpl<TicketProcessDao, TicketProcessEntity> implements TicketProcessService {

    private static final String APPROVE_TYPE = "approve_type";
    @Autowired
    @SuppressWarnings("all")
    private ProcessFeign processFeign;

    /**
     * 根据类型，调用去审核接口
     *
     * @param checkSubmit 审核提交的信息
     * @param approveType 申请类型
     * @throws SysException 业务类型
     */
    @Override
    public void addTicketProcess(ApprovalSubmitDto checkSubmit, String approveType) throws SysException {
        //拼接发送消息
        QueryWrapper<TicketProcessEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(ApproveTypeEnum.OPEN_EXAMING.getCode().equals(approveType), TicketProcessEntity::getTicketOpenId, checkSubmit.getId())
                .eq(ApproveTypeEnum.FINANCE.getCode().equals(approveType), TicketProcessEntity::getTicketFinaceId, checkSubmit.getId())
                .eq(TicketProcessEntity::getApproveType, approveType);
        TicketProcessEntity processEntity = this.getOne(queryWrapper);
        if (null == processEntity) {
            throw new SysException("审批流程不存在!");
        }
        checkSubmit.setApprovalId(processEntity.getApproveId());
        //调用流程服务的去审核
        Result result = processFeign.processSubmit(checkSubmit);
        if (ResultCode.SUCCESS.getCode() != result.getCode()) {
            throw new SysException(result.getMsg());
        }
    }

    /**
     * 分页查询
     *
     * @param params 查询参数
     * @return 分页数据
     */
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<TicketProcessEntity> page = this.page(
                new Query<TicketProcessEntity>().getPage(params),
                new QueryWrapper<>()
        );

        return new PageUtils(page);
    }

    /**
     * 修改
     *
     * @param ticketProcessUpdateDto 修改实体
     * @param user                   当前登录用户
     * @throws SysException 业务异常
     */
    @Override
    public void updateTicketProcessById(TicketProcessUpdateDto ticketProcessUpdateDto, User user) throws SysException {
        TicketProcessEntity ticketProcessEntity = new TicketProcessEntity();
        BeanUtils.copyProperties(ticketProcessUpdateDto, ticketProcessEntity);
        ticketProcessEntity.setUpdaterId(user.getId());
        if (!this.updateById(ticketProcessEntity)) {
            throw new SysException("A00002", "修改失败！");
        }
    }

    /**
     * 获取当前登录人是否有对票据审核的操作
     *
     * @param ticketIdList 票据id集合
     * @param approveType  审批类型枚举值
     * @return 对应每个票据id，是否有审核操作标识
     * @throws SysException 系统异常
     */
    @Override
    public List<Boolean> getUserApproveFlags(List<String> ticketIdList, ApproveTypeEnum approveType) throws SysException {
        QueryWrapper<TicketProcessEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.in(approveType.getApproveColumnName(), ticketIdList);
        queryWrapper.eq(APPROVE_TYPE, approveType.getCode());
        List<TicketProcessEntity> ticketProcessList = this.baseMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(ticketProcessList)) {
            return new ArrayList<>();
        }
        List<String> approveIdList = new ArrayList<>();
        Map<Integer, TicketProcessEntity> ticketProcessEntityMap = new HashMap<>(ticketProcessList.size());
        for (TicketProcessEntity ticketProcessEntity : ticketProcessList) {
            approveIdList.add(ticketProcessEntity.getApproveId());
            ticketProcessEntityMap.put(getApproveTypeTicketId(ticketProcessEntity, approveType), ticketProcessEntity);
        }
        Result approvalAuth = processFeign.getApprovalAuth(approveIdList);
        List<ApprovalAuthPar> approvalAuthParList = JSON.parseArray(JSON.toJSON(approvalAuth.getResult().get(APPROVAL_AUTH)).toString(), ApprovalAuthPar.class);
        if (CollectionUtils.isEmpty(approvalAuthParList)) {
            throw SysExceptionFactory.createSysException(PROCESS_FEIGN_GET_APPROVAL_AUTH_ERROR);
        }
        Map<Integer, List<ApprovalAuthPar>> approvalMap =
                approvalAuthParList.stream().collect(Collectors.groupingBy(ApprovalAuthPar::getApprovalId));
        List<Boolean> result = new ArrayList<>();
        for (String ticketId : ticketIdList) {
            TicketProcessEntity ticketProcessEntity = ticketProcessEntityMap.get(ticketId);
            if (ticketProcessEntity == null) {
                result.add(false);
                continue;
            }
            List<ApprovalAuthPar> approvalList = approvalMap.get(ticketProcessEntity.getApproveId());
            if (CollectionUtils.isEmpty(approvalList)) {
                throw SysExceptionFactory.createSysException(PROCESS_FEIGN_GET_APPROVAL_AUTH_ERROR);
            }
            result.add(ProcessConstant.approveFlag(approvalList.get(0).getIsShow()));
        }
        return result;
    }

    /**
     * 获取票据流程表对应审批类型的票据id
     * 就是获取ticketProcessEntity的approveType.getApproveColumnName()字段值
     *
     * @param ticketProcessEntity 票据审批对象
     * @param approveType         审批类型
     * @return 票据id
     */
    private Integer getApproveTypeTicketId(TicketProcessEntity ticketProcessEntity, ApproveTypeEnum approveType) throws SysException {
        String methodName = "get" + StrUtils.capitalize(StrUtils.lineToHump(approveType.getApproveColumnName()));
        Method method = ReflectionUtils.findMethod(ticketProcessEntity.getClass(), methodName);
        if (null != method) {
            Object o = ReflectionUtils.invokeMethod(method, ticketProcessEntity);
            if (null != o) {
                return (Integer) o;
            } else {
                throw SysExceptionFactory.createSysException(TICKET_PROCESS_GET_APPROVE_TYPE_TICKET_ID_VALUE_ERROR);
            }
        } else {
            throw SysExceptionFactory.createSysException(TICKET_PROCESS_NO_GET_APPROVE_TYPE_TICKET_ID_METHOD_FOUND_ERROR);
        }
    }

    /**
     * 设置票据流程表对应审批类型的票据id
     * 就是获取ticketProcessEntity的approveType.getApproveColumnName()字段值
     *
     * @param ticketProcessEntity 票据审批对象
     * @param approveType         审批类型
     */
    private void setApproveTypeTicketId(TicketProcessEntity ticketProcessEntity, ApproveTypeEnum approveType, String approveTypeTicketId)
            throws SysException {
        String methodName = "set" + StrUtils.capitalize(StrUtils.lineToHump(approveType.getApproveColumnName()));
        Method method = ReflectionUtils.findMethod(ticketProcessEntity.getClass(), methodName, String.class);
        if (null != method) {
            ReflectionUtils.invokeMethod(method, ticketProcessEntity, approveTypeTicketId);
        } else {
            throw SysExceptionFactory.createSysException(TICKET_PROCESS_NO_SET_APPROVE_TYPE_TICKET_ID_METHOD_FOUND_ERROR);
        }
    }

    /**
     * 发起流程审批
     *
     * @param approvalProcessAddDto 流程id集合
     * @param ticketIdList          开立申请id集合
     * @param user                  登录用户
     * @return boolean true代表流程开启，false 流程关闭，无需审批
     */
    @Override
    @SuppressWarnings("unchecked")
    public boolean saveBatchTicketProcess(ApprovalAddDto approvalProcessAddDto, List<String> ticketIdList, ApproveTypeEnum approveType, User user) throws SysException {
        Result processResult = processFeign.saveBachApprovalProcess(approvalProcessAddDto);
        if (ResultCode.SUCCESS.getCode() != processResult.getCode()) {
            throw SysExceptionFactory.createSysException(TICKET_PROCESS_FAIL_VERITY);
        }
        List<String> approvalIdList = (List<String>) processResult.getResult().get("approvalId");
        // 保存开立审批流信息 并且发送消息
        if (!CollectionUtils.isEmpty(approvalIdList)) {
            List<TicketProcessEntity> list = new ArrayList<>();
            for (int i = 0; i < approvalIdList.size(); i++) {
                TicketProcessEntity ticketProcessEntity = setTicketProcess(ticketIdList.get(i), approveType, user, approvalIdList.get(i), null);
                list.add(ticketProcessEntity);
            }
            this.saveBatch(list);
            return true;
        }
        return false;
    }

    /**
     * 设置流程属性值
     *
     * @param approveTypeTicketId 流程对应的票据id
     * @param approveType         审批类型
     * @param user                登录用户
     * @param approveId           审批id
     * @return 流程信息
     */
    private TicketProcessEntity setTicketProcess(String approveTypeTicketId, ApproveTypeEnum approveType, User user, String approveId, String enterpriseId) throws SysException {
        TicketProcessEntity ticketProcessEntity = new TicketProcessEntity();
        ticketProcessEntity.setId(UuidUtils.UUID());
        ticketProcessEntity.setApproveId(approveId);
        if (enterpriseId != null) {
            ticketProcessEntity.setEnterpriseId(enterpriseId);
        } else {
            ticketProcessEntity.setEnterpriseId(user.getEnterpriseId());
        }
        ticketProcessEntity.setCreatorId(user.getId());
        ticketProcessEntity.setUpdaterId(user.getId());
        ticketProcessEntity.setApproveType(approveType.getCode());
        ticketProcessEntity.setApproveStatus(ApproveProcessEnum.APPROVE_PROCESS_ING.getCode());
        this.setApproveTypeTicketId(ticketProcessEntity, approveType, approveTypeTicketId);
        return ticketProcessEntity;
    }

    /**
     * 发起流程审批
     *
     * @param ticketAmount        交易金额
     * @param approveTypeTicketId 开立申请id
     * @param msg                 消息内容json
     * @param approveTypeEnum     审批类型枚举
     * @param user                登录用户
     * @param enterpriseId        签收方企业id
     * @return boolean true代表流程开启，false 流程关闭，无需审批
     * @throws SysException 调用RPC失败，获取或设置流程对应业务票据id时可能抛出异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveTicketProcess(Double ticketAmount, String approveTypeTicketId, String msg,
                                     ApproveTypeEnum approveTypeEnum, User user, String enterpriseId, String applyType) throws SysException {
        //   content内容拼接
        ApprovalAddDto addDto = new ApprovalAddDto();
        addDto.setMsg(msg);
        if(approveTypeEnum.getCode().equals(ApproveTypeEnum.SIGN_TRANSFER.getCode())){
            addDto.setApprovalType(ApproveTypeEnum.SIGN_OPEN_TRANSFER.getCode());
        }else {
            addDto.setApprovalType(approveTypeEnum.getCode());
        }
        addDto.setEnterpriseId(enterpriseId);
        addDto.setApplyId(approveTypeTicketId);
        addDto.setApplyType(applyType);
        Result processResult = processFeign.save(addDto);
        if (ResultCode.SUCCESS.getCode() != processResult.getCode()) {
            throw new SysException(processResult.getMsg());
        }
        String approvalId = (String) processResult.getResult().get("approvalId");
        // 保存开立审批流信息
        if (null != approvalId) {
            TicketProcessEntity ticketProcessEntity = setTicketProcess(approveTypeTicketId, approveTypeEnum, user, approvalId, enterpriseId);
            this.save(ticketProcessEntity);
            return true;
        }
        return false;
    }

    /**
     * 回滚发起流程审批
     *
     * @param ticketAmount        交易金额
     * @param approveTypeTicketId 开立申请id
     * @param msg                 消息内容json
     * @param approveTypeEnum     审批类型枚举
     * @param user                登录用户
     * @param enterpriseId        签收方企业id
     * @return boolean true代表流程开启，false 流程关闭，无需审批
     * @throws SysException 调用RPC失败，获取或设置流程对应业务票据id时可能抛出异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean rollbackSaveTicketProcess(Double ticketAmount, String approveTypeTicketId, String msg,
                                             ApproveTypeEnum approveTypeEnum, User user, String enterpriseId) throws SysException {

        return true;
    }

    /**
     * 打断票据审批流程
     * 例如：撤销业务申请时
     *
     * @param ticketId         票据id
     * @param approveTypeEnum  审批类型字典值
     * @param sysExceptionEnum 调用P服务终止审批流失败时抛出的异常
     * @throws SysException 调用P服务终止审批流时可能抛出异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void interruptTicketProcess(String ticketId, ApproveTypeEnum approveTypeEnum, SysExceptionEnum sysExceptionEnum) throws SysException {
        //3 查询审批中的流程，修改成终止状态
        TicketProcessEntity ticketProcessEntity = this.getTicketProcess(ticketId, approveTypeEnum);
        if (ticketProcessEntity != null) {
            //3.2 将审核流程变成已撤销状态
            TicketProcessEntity processEntity = new TicketProcessEntity();
            processEntity.setId(ticketProcessEntity.getId());
            processEntity.setApproveStatus(ApproveProcessEnum.APPROVE_PROCESS_CANCEL.getCode());
            this.updateById(processEntity);
            //3.1 调用第三方接口，将进行中的流程终止
            Result result = processFeign.cancel(ticketProcessEntity.getApproveId());
            if (ResultCode.SUCCESS.getCode() != result.getCode()) {
                throw SysExceptionFactory.createSysException(sysExceptionEnum);
            }
        }
    }

    /**
     * 打断票据审批流程
     * 例如：撤销业务申请时
     *
     * @param ticketId        票据id
     * @param approveTypeEnum 审批类型字典值
     */
    private TicketProcessEntity getTicketProcess(String ticketId, ApproveTypeEnum approveTypeEnum) {
        //3 查询审批中的流程，修改成终止状态
        QueryWrapper<TicketProcessEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(approveTypeEnum.getApproveColumnName(), ticketId)
                .eq(APPROVE_TYPE, approveTypeEnum.getCode())
                .eq("approve_status", ApproveProcessEnum.APPROVE_PROCESS_ING.getCode())
                .orderByDesc("create_time");
        List<TicketProcessEntity> list = this.list(queryWrapper);
        if (!CollectionUtils.isEmpty(list)) {
            return list.get(0);
        }
        return null;
    }

    /**
     * 发送审批到P服务
     *
     * @param ticket            票据对象
     * @param approveTypeEnum   审批类型
     * @param approvalSubmitDto 审批数据对象
     * @throws SysException 业务异常
     */
    @Override
    public void approvalSubmit(Object ticket, ApproveTypeEnum approveTypeEnum, ApprovalSubmitDto approvalSubmitDto) throws SysException {
        // 如果 转让的票被锁定则 不用发 rpc
        String ticketId = TicketUtils.getId(ticket);
        TicketProcessEntity ticketProcessEntity = this.getTicketProcess(ticketId, approveTypeEnum);
        if (ticketProcessEntity != null) {
            approvalSubmitDto.setApprovalId(ticketProcessEntity.getApproveId());
            processFeign.processSubmit(approvalSubmitDto);
        }
    }

    /**
     * 获取流程进度数据
     *
     * @param ticketId        票据id
     * @param approveTypeEnum 审批类型
     * @param enterpriseId    企业id
     * @return 流程进度集合
     * @throws SysException RPC接口可能抛出异常
     */
    @Override
    @SuppressWarnings("unchecked")
    public List<ApUserShowInfo> getApUserShowInfoList(String ticketId, ApproveTypeEnum approveTypeEnum, String enterpriseId) throws SysException {
        //2 查看审核节点和申请人信息，查询当前节点所有审批流程
        QueryWrapper<TicketProcessEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(approveTypeEnum.getApproveColumnName(), ticketId)
                .eq(APPROVE_TYPE, approveTypeEnum)
                .eq("enterprise_id", enterpriseId)
                .orderByDesc("create_time");
        //同一个票，同时只能有一个流程在进行中，故取最新的数据
        TicketProcessEntity ticketProcessEntity = this.getOne(queryWrapper);
        if (null != ticketProcessEntity) {
            Result apUserShow = processFeign.getApUserShow(ticketProcessEntity.getApproveId());
            if (ResultCode.SUCCESS.getCode() != apUserShow.getCode()) {
                throw new SysException("流程服务" + apUserShow.getMsg());
            }
            Object list = apUserShow.getResult().get("list");
            if (null != list) {
                return JSON.parseObject(JSON.toJSONString(list), List.class);
            }
        }
        return new ArrayList<>(0);
    }


    /**
     * 获取流程进度数据
     *
     * @return 流程进度集合
     * @throws SysException RPC接口可能抛出异常
     */
    @Override
    @SuppressWarnings("unchecked")
    public List<ApUserShowInfo> getApUserShowInfoList(TicketProcessEntity ticketProcessEntity) throws SysException {
        /*TODO*/
        return new ArrayList<>(0);
    }
}